package com.portal.incurridos.converter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.List;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.portal.incurridos.persistence.enumeration.HeaderApplicationEnum;
import com.portal.incurridos.persistence.enumeration.ValueHeaderApplicationEnum;

/**
 * Interceptor para las entradas y salidas de las peticiones
 * de los servicios.
 * @author Alan Dávila
 * e-mail ustarga@gmail.com
 * date Jun 19, 2013 8:15:55 PM
 */
public class GsonHttpMessageConverter
extends AbstractHttpMessageConverter<Object> {

	/**
	 * Enconding for text sent into request.
	 */
	private static final String UTF8 = "UTF-8";
    /**
     * Lista de clases soportadas para unmarshall y marshall
     * objects.
     */
	private List<Class<?>> soportedClass;
	/**
     * @return the soportedClass
     */
    public final List<Class<?>> getSoportedClass() {
        return soportedClass;
    }
    /**
	 * Variable para tratar los objetos como json.
	 */
	private Gson gson;
	/**
	 * Constructor por defecto.
	 */
	public GsonHttpMessageConverter() {
		super(new MediaType(HeaderApplicationEnum.CONTENT_TYPE.getValue(),
				ValueHeaderApplicationEnum.JSON.getValue(),
				Charset.forName(UTF8)));
		GsonBuilder gsonBuilder;
		//Si se quiere se pueden agregar adaptadores
		gsonBuilder = new GsonBuilder();
		gson = gsonBuilder.create();
	}

	@Override
    protected final boolean supports(final Class<?> clazz) {
		if (soportedClass.contains(clazz)) {
			return Boolean.TRUE;
		}
		return Boolean.FALSE;
	}

	@Override
	protected final Object readInternal(final Class<? extends Object> clazz,
			final HttpInputMessage inputMessage) throws IOException,
			HttpMessageNotReadableException {
		try {
			String json = convertStreamToString(inputMessage.getBody());
            return gson.fromJson(json, clazz);
        } catch (JsonSyntaxException e) {
            throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e);
        }
	}

	@Override
	protected final void writeInternal(final Object t,
	        final HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {
	    if (!supports(t.getClass())) {
	        throw new HttpMessageNotWritableException("Not Writable:" + t.getClass());
	    }
	    String json = gson.toJson(t);
        outputMessage.getBody().write(json.getBytes());
	}

	/**
	 * Asgina un nuevo valor para la lista de clases soportadas.
	 * @param clases Lista de clases soportadas.
	 */
	public final void setSoportedClass(final List<Class<?>> clases) {
		this.soportedClass = clases;
	}
	/**
	 * Convierte a String un origen de datos.
	 * @param is InputStream con los datos.
	 * @return String con los datos.
	 * @throws IOException Can't open the buffered.
	 */
	private String convertStreamToString(final InputStream is) throws IOException {
        /*
         * To convert the InputStream to String we use the Reader.read(char[]
         * buffer) method. We iterate until the Reader return -1 which means
         * there's no more data to read. We use the StringWriter class to
         * produce the string.
         */
        if (is != null) {
            Writer writer = new StringWriter();

            char[] buffer = new char[1024];
            try {
                Reader reader = new BufferedReader(new InputStreamReader(is, UTF8));
                int n;
                while ((n = reader.read(buffer)) != -1) {
                    writer.write(buffer, 0, n);
                }
            } finally {
                is.close();
            }
            return writer.toString();
        } else {
            return "";
        }
    }
}
